/*file chiron src/modules.ng.js */
/*preamble

    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    The license terms are stated in full in <license.rst> and at the end
    of all source files.

*/

/**
*/

/*
    isolate the module loader in an enclosure by creating an
    annonymous function and then--at the end of this file--
    calling it.

    also, take the evalGlobal function as an argument so that it can be
    declared in global scope.  this prevents the eval function
    from inheriting variables from the modulesjs enclosure scope.
*/
(function (evalGlobal) {

    var beginTime = new Date();

    /*
        `mainUrl` is the URL from the <script> tag that executed this script.
        We use thie URL to infer the `moduleRootURl` and to obtain a list
        of "main" modules to execute from its query-string.
    */
    var mainUrl;

    function main () {

        /* mark the time */
        readylib.beginTime = beginTime;

        /*
            find and remove the `mainUrl` <script> tag out of the DOM.

            A user could potentially use more than one copy of `modules.js`
            in a single document.  Because of this subtle behavior recommended
            by Mark Miller, we are able to leave no evidence that our module
            loader is running for other module loaders to discover it, nor can
            we discover any of the other module loaders.  For one, this
            module loader leaves no references to itself on the "window" object.
            Also, <script> tags are executed as they appear in the DOM: at the
            at which this "main" function runs, we cannot discover any <script>
            tags that may be added to the DOM after this <script> finishes
            executing.  Also, we remove our own <script> tag from the DOM,
            so no future <script> tags can look back at the DOM to discover
            this module laoder's "mainUrl".  This is a subtle point and not
            one that you can alone use to ensure security between two sandboxes,
            but if that's you're intent, this module loader may help.
        */
        var scripts = document.getElementsByTagName("script");
        for (var i = 0; i < scripts.length; i++) {
            var script = scripts[i];
            if (browserlib.hasAttribute(script, "src")) {
                var src = script.getAttribute("src");
                srcObject = urllib.parse(src);
                if (srcObject.file == "modules.js") {
                    script.parentNode.removeChild(script);
                    mainUrl = urllib.urlJoin(window.location.href, src);
                    modulelib.init();
                    break;
                }
            }
        }

        /* listen for dom readiness */
        readylib.observe(evallib.basin(ready));

    }

    function ready () {

        /* mark the time */
        readylib.readyTime = new Date();

        /* note for CSS that JavaScript is enabled */
        document.body.className = document.body.className + ' javascript';

        /* run all of the "main" scripts in the mainUrl's query-string */
        var mainUrlObject = urllib.parse(mainUrl);
        if (mainUrlObject.query != "") {
            var urls = mainUrlObject.query.split("&");
            for (var i = 0; i < urls.length; i++) {
                var url = urls[i];
                modulelib.require(url);
            }
        }

        /* mark the time */
        readylib.endTime = new Date();

        /* make a note to the console that the module loader is done */
        browserlib.print('ready', 'info');

    };

    /*** modulelib
    */
    var modulelib = new function () {

        /**** init
        */
        this.init = function () {
            modulelib.moduleRoot = urllib.urlJoin(mainUrl, '');
        };

        /**** include
        */
        this.include = function (url, structure, sandbox) {
            var module = this;
        };

        /**** require
        */
        this.require = function (url, structure, sandbox) {
            var module = this;
        };

        /**** register
        */
        this.register = function () {
            var module = this;
        };

        /**** publish
        */
        this.publish = function () {
            var module = this;
        };

        /**** foreignModuleBind
        */
        this.foreignModuleBind = function (callback) {
            callback.foreignModuleBind = true;
            return callback;
        };


        /**** fetch
        */

        /**** local
        */
        this.createLocal = function (module) {
            module.local = {
                'include': baselib.bind(modulelib.include, module),
                'require': baselib.bind(modulelib.require, module),
                'register': baselib.bind(modulelib.register, module),
                'publish': baselib.bind(modulelib.publish, module),
                'foreignModuleBind': modulelib.foreignModuleBind,
                'print': browserlib.print,
                'module': module
            };
            return module.local;
        };

        /**** moduleConstructor
        */
        this.moduleConstructor = function () {
        };

        /**** moduleConstructors
        */
        this.moduleConstructors = {};

        /**** dub
        */


    };

    /*** baselib
    */
    var baselib = new function () {

        /**** bind
        */
        this.bind = function (callback, that) {
            return function () {
                return callback.apply(that, arguments);
            };
        };

        /**** destructure
            destructures an object based on a given structure specification.  If the structure
            is an `Array`, the values of the array are treated as keys from the original object
            and only those items are copied.  If the structure is an `Object`, the keys are
            correspond to keys in the object and the values are treated as keys for the corresponding
            result.  Using an `Object` allows you to remap keys from the object to the result.  If
            the structure is `undefined`, the result is a full copy of the original object.
            `destructure` always returns a new `Object`.

             - accepts an ``object``
             - accepts an optional ``structure`` that is `undefined`, an `Array`, or an `Object`.
             - returns a possibly partial copy of the object based on the ``structure``.

        */
        this.destructure = function (object, structure) {
            var i, key, result = {};
            if (structure === undefined) {
                for (key in object) {
                    if (object.hasOwnProperty(key)) {
                        result[key] = object[key];
                    }
                }
            } else if (structure instanceof Array) {
                for (i = 0; i < structure.length; i++) {
                    key = structure[i];
                    result[key] = object[key];
                }
            } else if (typeof structure == 'object') {
                for (key in structure) {
                    if (structure.hasOwnProperty(key)) {
                        value = structure[key];
                        result[value] = object[key];
                    }
                }
            } else {
                throw new Error("Structures must either be arrays, objects, or undefined");
            }
            return result;
        };

    };

    /*** evallib
    */
    var evallib = new function () {

        /**** evalGlobal
        */
        this.evalGlobal = evalGlobal;

        /**** evalGlobalWith
        */
        this.evalGlobalWith = function () {
            /*
                construct a text that can be evaluated within a naked global
                eval that will accept this text and in turn eval the next
                argument, our given inner text (arguments[0] here,
                arguments[1] inside)
            */
            var text = "(function () {" +
                "return eval(arguments[1])" + 
            "}).apply(this, arguments)";

            /*
                wrap the eval in a with block for every scope from inside to out
                provided as additional, variadic arguments
            */
            for (var i = 1; i < arguments.length; i++) {
                text = "with (arguments[" + (i + 1) + "]) { " + text + " }";

            }

            /*
                pass all of the same arguments to `evalGlobal` but add
                the "text" to the beginning
            */
            var args = [text];

            /*
                `push.apply` is faster than `concat` since it doesn't need
                to construct a new Array
            */
            args.push.apply(args, arguments);

            return evalGlobal.apply(this, args);
        };

        /**** basin
            decorates a function such that instead of raising exceptions,
            it will print error messages.  `basin` preserves binding.
        */
        this.basin = function (callback) {
            return function () {
                try {
                    callback.apply(this, arguments);
                } catch (exception) {
                    browserlib.print(
                        exception.message ? exception.message : '' + exception,
                        'error'
                    );
                }
            };
        };

    };

    /*** browserlib
    */
    var browserlib = new function () {

        /**** print

            accepts:
             - a ``message`` and 
             - an optional ``label``.

            The label, by convention, is one of `"log"``, ``"info"``,
            ``"warn"``, or ``"error"``.  Custom loggers treat labels like
            ``"module"``, ``"pass"``, or ``"fail"``.  Attempts to write
            the message to `window.console`, progressively handling
            `console` implementations that provide a function for the 
            given ``label``, or defaulting to `log` depending on
            availability.

            Also adds a ``[message, label]`` array to the end
            of `browserlib.printHistory`.

            In Safari, `print` writes to the Javascript debug console, which
            is only available if you set the preference::

                defaults write com.apple.Safari IncludeDebugMenu 1

            Or in Safari 3::

                defaults write com.apple.Safari IncludeDevelopMenu 1

            In Firefox, you can get a debug console with Firebug,
            http://getfirebug.com.

        */
        this.print = function (message, label) {
            label = arguments.length == 1 ? "log" : label;
            browserlib.printHistory.push(arguments);
            if (window.console) {
                if (console.print) {
                    console.print(message, label);
                } else if (console[label]) {
                    console[label](message);
                } else {
                    console.log(message);
                }
            }
        };

        /**** printHistory
        */
        this.printHistory = [];

        /**** isOpera
            whether the browser is Opera.
        */
        this.isOpera = !!window.opera;

        /**** isIE
            whether the browser is Internet Explorer.
            checks for Opera faking to be IE.

            This name deviates from the Chiron naming convention
            because of popularity for the term isIE.
        */
        this.isIE = !this.isOpera && navigator.userAgent.indexOf("MSIE") >= 0;

        /**** isWebkit
            checks whether the browser is Webkit, albeit
            the brwoser is Safari, Chrome, or modern Konqueror.
        */
        this.isWebkit = navigator.userAgent.indexOf("WebKit") >= 0;

        /**** isKhtml
            checks for the old KHTML engine for versions of Konqueror.
            These have been largely replaced by WebKit, but we still have
            to check to verify that it's not Gecko.
        */
        this.isKhtml = navigator.userAgent.indexOf("KHTML") >= 0;

        /**** isGecko
            Checks for Konqueror masquerading as Gecko.
        */
        this.isWebkit = !this.isKhtml && navigator.userAgent.indexOf("Gecko") >= 0;

        /**** hasAttribute
            corss browser implementation of DOM.hasAttribute
        */
        if (this.isIE) {
            this.hasAttribute = function (element, key) {
                var node = element.getAttributeNode(key);
                return node && node.specified;
            };
        } else {
            this.hasAttribute = function (element, key) {
                return element.hasAttribute(key);
            };
        }

    };

    /*** urllib
    */
    var urllib = new function () {

        /**** urlJoin
             - returns the destination URL following a path
               of URL's given as variadic arguments.

            ``urlJoin`` is analogous to Python's ``urllib.urljoin``.
        */
        this.urlJoin = function (base /* relative, ... */) {
            for (var i = 1; i < arguments.length; i++) {
                base = urllib.resolve(arguments[i], base);
            }
            return base;
        };

        /**** resolve 
             - accepts a ``relative`` URL
             - accepts a ``base`` URL
             - returns a URL resovled to a relative URL from a base URL.
        */
        this.resolve = function (relative, base) {
            return urllib.format(urllib.resolveObject(relative, base));
        };

        /**** resolveObject
             - accepts a ``relative`` URL
             - accepts a ``base`` URL
             - returns an object representing a URL resolved from
               a relative location and a base location.
        */
        this.resolveObject = function (relative, base) {
            if (!base) base = window.location.href;

            base = urllib.parse(base);
            relative = urllib.parse(relative);

            if (relative.url == "")
                return base;

            delete base.url;
            delete base.authority;
            delete base.domain;
            delete base.userInfo;
            delete base.path;
            delete base.directory;

            if (
                relative.protocol && relative.protocol != base.protocol ||
                relative.authority && relative.authority != base.authority
            ) {
                base = relative;
            } else {
                if (relative.root) {
                    base.directories = relative.directories;
                } else {

                    var directories = relative.directories;
                    for (var i = 0; i < directories.length; i++) {
                        var directory = directories[i];
                        if (directory == ".") {
                        } else if (directory == "..") {
                            if (base.directories.length) {
                                base.directories.pop();
                            } else {
                                base.directories.push('..');
                            }
                        } else {
                            base.directories.push(directory);
                        }
                    }

                    if (relative.file == ".") {
                        relative.file = "";
                    } else if (relative.file == "..") {
                        base.directories.pop();
                        relative.file = "";
                    }
                }
            }

            if (relative.root)
                base.root = relative.root;
            if (relative.protcol)
                base.protocol = relative.protocol;
            if (!(!relative.path && relative.anchor))
                base.file = relative.file;
            base.query = relative.query;
            base.anchor = relative.anchor;

            return base;
        };

        /**** relative
            returns a relative URL to a target from a source.
        */
        this.relative = function (target, base) {
            return urllib.format(urllib.relativeObject(target, base));
        };

        /**** relativeObject
            returns an object representing a relative URL to
            a given target URL from a source URL.
        */
        this.relativeObject = function (target, base) {
            target = urllib.parse(target);
            base = urllib.parse(base);

            delete target.url;

            if (
                target.protocol == base.protocol &&
                target.authority == base.authority
            ) {
                delete target.protocol;
                delete target.authority;
                delete target.userInfo;
                delete target.user;
                delete target.password;
                delete target.domain;
                delete target.domains;
                delete target.port;
                if (
                    !!target.root == !!base.root && !(
                        target.root &&
                        target.directories[0] != base.directories[0]
                    )
                ) {
                    delete target.path;
                    delete target.root;
                    delete target.directory;
                    while (
                        base.directories.length &&
                        target.directories.length &&
                        target.directories[0] == base.directories[0]
                    ) {
                        target.directories.shift();
                        base.directories.shift();
                    }
                    while (base.directories.length) {
                        base.directories.shift();
                        target.directories.unshift('..');
                    }

                    if (!target.root && !target.directories.length && !target.file && base.file)
                        target.directories.push('.');

                    if (base.file == target.file)
                        delete target.file;
                    if (base.query == target.query)
                        delete target.query;
                    if (base.anchor == target.anchor)
                        delete target.anchor;
                }
            }

            return target;
        };

        /**** groupNames
        */
        this.groupNames = [
            "url",
            "protocol",
            "authorityRoot",
            "authority",
                "userInfo",
                    "user",
                    "password",
                "domain",
                "port",
            "path",
                "root",
                "directory",
                "file",
            "query",
            "anchor"
        ];

        /**** expression
        */
        this.expression = new RegExp( /* url */
            "^" +
            "(?:" +
                "([^:/?#]+):" + /* protocol */
            ")?" +
            "(?:" +
                "(//)" + /* authorityRoot */
                "(" + /* authority */
                    "(?:" +
                        "(" + /* userInfo */
                            "([^:@]*)" + /* user */
                            ":?" +
                            "([^:@]*)" + /* password */
                        ")?" +
                        "@" +
                    ")?" +
                    "([^:/?#]*)" + /* domain */
                    "(?::(\\d*))?" + /* port */
                ")" +
            ")?" +
            "(" + /* path */
                "(/?)" + /* root */
                "((?:[^?#/]*/)*)" +
                "([^?#]*)" + /* file */
            ")" +
            "(?:\\?([^#]*))?" + /* query */
            "(?:#(.*))?" /*anchor */
        );

        this.parser = function (expression, groupNames) {
            return function (url) {
                if (typeof url != "string")
                    throw new Error("TypeError: urllib: parse: URL's msut be Strings.");

                var urlParts = {};

                var groups = expression.exec(url);

                for (var i = 0; i < groups.length; i++) {
                    urlParts[urllib.groupNames[i]] = groups[i] ? groups[i] : "";
                }

                urlParts.root = (urlParts.root || urlParts.authorityRoot) ? '/' : '';

                urlParts.directories = urlParts.directory.split("/");
                if (urlParts.directories[urlParts.directories.length - 1] == "") {
                    urlParts.directories.pop();
                }

                /* normalize */
                var directories = [];
                for (var i = 0; i < urlParts.directories.length; i++) {
                    var directory = urlParts.directories[i];
                    if (directory == '.') {
                    } else if (directory == '..') {
                        if (directories.length && directories[directories.length - 1] != '..')
                            directories.pop();
                        else
                            directories.push('..');
                    } else {
                        directories.push(directory);
                    }
                }
                urlParts.directories = directories;

                urlParts.domains = urlParts.domain.split(".");

                return urlParts;
            };
        };

        /**** parse
        */
        this.parse = this.parser(this.expression, this.groupNames);

        /**** format
        */
        this.format = function (object) {
            if (typeof(object) != 'object')
                throw new Error("urllib: format: only URL objects can be formatted");

            var domain =
                object.domains ?
                object.domains.join(".") :
                object.domain;

            var userInfo = (
                    object.user ||
                    object.password 
                ) ?
                (
                    (object.user || "") + 
                    (object.password ? ":" + object.password : "") 
                ) :
                object.userInfo;

            var authority = (
                    userInfo ||
                    domain ||
                    object.port
                ) ? (
                    (userInfo ? userInfo + "@" : "") +
                    (domain || "") + 
                    (object.port ? ":" + object.port : "")
                ) :
                object.authority;

            var directory =
                object.directories ?
                object.directories.join("/") :
                object.directory;

            var path =
                directory || object.file ?
                (
                    (directory ? directory + "/" : "") +
                    (object.file || "")
                ) :
                object.path;

            return (
                (object.protocol ? object.protocol + ":" : "") +
                (authority ? "//" + authority : "") +
                (object.root || (authority && path) ? "/" : "") +
                (path ? path : "") +
                (object.query ? "?" + object.query : "") +
                (object.anchor ? "#" + object.anchor : "")
            ) || object.url || "";
        };

    };

    /*** httplib
    */
    var httplib = new function () {

        /**** requestText
            returns the text at a given URL using an HTTP
            request.
            supports continuation passing form for asynchronous
            requests.
        */
        this.requestText = function (url, observer) {
            if (observer !== undefined) {
                return httplib.request(url, function (response) {
                    if (response.isError())
                        observer(undefined, new Error(response.getStatus()));
                    else
                        observer(response.getText());
                });
            } else {
                var response = httplib.request(url);
                if (response.isError())
                    throw new Error(response.getStatus());
                return response.getText();
            }
        };

        /**** synchronous
            a constant for specifying scnchronous, blocking,
            waiting HTTP requests.
        */
        this.synchronous = false;

        /**** asynchronous
            a constant for specifying asynchronous, non-blocking
            HTTP requests.
        */
        this.asynchronous = true;

        /**** request
            sends an HTTP request to a given URL and returns
            the response.
            supports continuation passing form for asynchronous
            requests.
        */
        this.request = function (url, observer) {
            var request = httplib.Request();
            var response = request.getResponse();

            url = urllib.resolve(url, window.location.href);

            if (observer)
                request.observe("ok", observer);

            request.open("GET", url, !!observer);

            try {
                request.send();
            } catch (exception) {
                request.abort();
                throw new Error('HttpError: "' + url + '": ' + exception);
            }

            if (observer !== undefined) {
                return request;
            } else {
                return response;
            }

        };

        /*todo
            Look deeper into dojo/src/hostenv_browser.js for 
            _blockAsync lock to prevent eternal hanging in KHTML
        */

        /**** Request
            returns a wrapped HTTP Request object.
        */
        this.Request = function () {
            /* this line permits the user to create a request with
             * either new Request() or Request().  internally,
             * we just use the Request() so that Request can be
             * overloaded later in http.js to be a type constructor
             * function instead of a prototype constructor */
            if (this == httplib) return new httplib.Request();

            var self = this;
            var method, url, asynchronous, user, password;
            var realRequest = httplib.createNativeRequest();
            var response = httplib.Response(realRequest);
            var isOpen = false;
            var readyStateChanged;
            var timeout = 1000;

            var readyFunctors = [];
            var okFunctors = [];
            var errorFunctors = [];
            var warnFunctors = [];

            /***** toString
            */
            self.toString = function () {return "[object HttpRequest]"};

            /***** getResponse
            */
            self.getResponse = function () {
                return response;
            };

            var signal = function (response, observers) {
                while (observers.length) {
                    var observer = observers.shift();
                    observer(response);
                }
            };

            /***** pogress
                an event function that the Request calls when it
                receives a chunk of content.
            */
            self.progress = function () {
                /* necessary: this function becomes an observable signal */
            };

            /***** ready
                an event function that the Request calls when
                the Reponse is ready.
            */
            self.ready = function () {
                signal(response, readyFunctors);
            };

            /***** ok
                an event function that the Request calls when a Response
                is ready and all went well.
            */
            self.ok = function () {
                signal(response, okFunctors);
            };

            /***** error
                an event function that the Request calls when a Reponse
                is completed but failed to retrieve the requested content.
            */
            self.error = function () {
                signal(response, errorFunctors);
            };

            /***** warn
                an event function that the Request calls when 
                something is amiss with message.
            */
            self.warn = function (message) {
                warn(message);
                signal(response, warnFunctors);
            };

            /***** observe
                permits a user to observe `ready`, `ok`,
                `error`, and `warn` events with a handler
                function.
            */
            self.observe = function (eventName, observer) {
                if (eventName == "ready") readyFunctors.push(observer);
                else if (eventName == "ok") okFunctors.push(observer);
                else if (eventName == "error") errorFunctors.push(observer);
                else if (eventName == "warn") warnFunctors.push(observer);
                else throw new Error(
                    "HttpError: event name '" + eventName + "' " +
                    "is not recognized"
                );
            };

            /***** setHeader
            */
            self.setHeader = function (key, value) {
                realRequest.setRequestHeader(key, value);
            };

            /***** isOpen
            */
            self.isOpen = function () {
                return isOpen;
            };

            /***** isSent
            */
            self.isSent = function () {
                return realRequest.readyState > 0;
            };

            /***** getTimeout
            */
            self.getTimeout = function () {
                return timeout;
            };

            /***** setTimeout
            */
            self.setTimeout = function (value) {
                timeout = value;
            };

            /***** open

                Accepts

                 - ``method``, an HTTP request method, for example,
                   ``GET``, ``POST``, ``PROPFIND`` and others.
                 - ``url``, a web location string
                 - ``synchronous``, whether ``send`` will block until completed,
                   for example, ``httplib.synchronous``, ``httplib.asynchronous``.
                 - ``user``, an optional HTTP user name.
                 - ``password``, an optional HTTP password.

            */
            self.open = function (_method, _url, _asynchronous, _user, _password) {
                try {
                    return realRequest.open(
                        method = _method,
                        url = _url,
                        asynchronous = _asynchronous,
                        user = _user,
                        password = _password
                    );
                } finally {
                    isOpen = true;
                }
            };

            /***** send
                Accepts an optional ``content`` argument for requests like ``POST`` method.
            */
            self.send = function (content) {

                realRequest.setRequestHeader("X-Requested-With", "XMLHttpRequest");

                if (!content) {
                    content = "";
                }

                setTimeout(function () {
                    if (
                        !readyStateChanged &&
                        response.getStatus() != 200 &&
                        response.getStatus() !== undefined
                    ) {
                        self.error(response, 'HTTP request timed out.');
                        //self.abort();
                    }
                }, timeout);

                return realRequest.send(content);
            };

            /***** abort
            */
            self.abort = function () {
                return realRequest.abort();
            };

            /*
                todo consider using this snippet for uniform caching behavior

                From: http://en.wikipedia.org/wiki/XMLHttpRequest#Caching

                var request = (typeof(XMLHttpRequest) != "undefined") ?
                    new XMLHttpRequest() : new ActiveXObject("Msxml2.XMLHTTP");
                request.open("GET", url, false);
                request.send(null);
                if(!request.getResponseHeader("Date"))
                {
                  var cached = request;
                  request = (typeof(XMLHttpRequest) != "undefined") ?
                      new XMLHttpRequest() : new ActiveXObject("Msxml2.XMLHTTP");
                  var ifModifiedSince = cached.getResponseHeader("Last-Modified");
                  ifModifiedSince = (ifModifiedSince) ?
                      ifModifiedSince : new Date(0); // January 1, 1970
                  request.open("GET", url, false);
                  request.setRequestHeader("If-Modified-Since", ifModifiedSince);
                  request.send("");
                  if(request.status == 304)
                  {
                    request = cached;
                  }
                }
            */

            realRequest.onreadystatechange = function () {
                readyStateChanged = true;

                self.progress();

                if (realRequest.readyState == 4) {
                    try {

                        self.ready(response);

                        if (response.isOk()) {
                            self.ok(response);
                        } else {
                            self.error(response);
                        }

                        /* this was a concern a while back; messages were getting truncated.
                         * it was only in Firefox with a Twisted server.  The problem does
                         * not appear to occur with static content, but this might be
                         * a handy check again if the problem crops up */
                        /*
                        if (
                            environment.isGecko &&
                            realRequest.responseText.length > 8 * 1024
                        ) {
                            self.warn('Possible HTTP Response overflow');
                        }
                        */

                    } catch (exception) {
                        error(exception.message || exception);
                    }
                }
            };

            return self;
        };

        /**** Response
            returns a wrapped HTTP Response object.
        */
        this.Response = function (realRequest) {
            /* this line permits the user to create a request with
             * either new Respones() or Response().  internally,
             * we just use the Response() so that Response can be
             * overloaded later in http.js to be a type constructor
             * function instead of a prototype constructor */
            if (this == httplib) return new httplib.Response(realRequest);

            var self = this;

            /* this init function doesn't get invoked until Response becomes
             * a type in http.js.  so, this method is merely for the future. */
            self.init = function (realRequestValue) {
                realRequest = realRequestValue;
            };

            /***** isReady
                whether the request is finished.  This indicates
                whether you can call `getStatus`
            */
            self.isReady = function () {
                return realRequest.readyState == 4;
            };

            /***** getStatus
                returns the HTTP response code.  Local files
                return 0.  Returns ``undefined`` if the
                underlying XML HTTP request throws an exception,
                `getStatus` returns ``undefined``.
            */
            self.getStatus = function () {
                /* one wouldn't think this were necessary.
                 * one would be wrong. */
                try {
                    return realRequest.status;
                } catch (exception) {
                    warn("HTTP status not available");
                    return undefined;
                }
            };

            /***** isOk
                returns whether a request had a valid response.
                This usually is indicative of a 200 HTTP response
                code, but there are variations among browsers.

                HTTP Status codes in the interval [200, 300] are all legal
                HTTP Ok responses. 

                In Firefox and Safari 3, local files acquired with an HTTP request
                have a status code of 0.

                In Safari 2, local files acquired with an asynchronous HTTP
                request have a status of undefined.

                In Safari, a response with no content causes a status
                of `undefined`.

                Konqueror requires acceptance of 304, "using cache",
                according to dojo/src/hostenv_browser.js

            */
            self.isOk = function () {
                var status = self.getStatus();
                return (
                    /* usually */
                    status >= 200 && status < 300 ||
                    /* Safari 2 asynchronous file:// and
                      all Safari for no file content */
                    status == undefined ||
                    /* Firefox and Safari 3 file:// */
                    status == 0 ||
                    /* Konqueror using cache */
                    status == 304
                );
            };

            /***** isError
            */
            self.isError = function () {
                return !self.isOk();
            };

            /***** getText
            */
            self.getText = function () {
                return realRequest.responseText;
            };

            /***** getXml
            */
            self.getXml = function () {
                /*todo account for Explorer's lack of responseXML using
                the ActiveX provided by Dean Edwards */
                if (realRequest.responseXML == undefined) {
                    throw new Error("HttpError: HTTP Response is not XML");
                }
                return realRequest.responseXML;
            };

            /***** getDocument
            */
            self.getDocument = function () {
                return self.getXml().documentElement;
            };

            /***** getHeader
            */
            self.getHeader = function (key) {
                return realRequest.getResponseHeader(key);
            };

            /***** hasHeader
            */
            self.hasHeader = function (key) {
                return realRequest.getResponseHeader(key) != undefined;
            };

            /***** getHeaders
            */
            self.getHeaders = function () {
                var headers = realRequest.getAllResponseHeaders();
                if (!headers) return {};
                return headers;
            };

            /***** len
            */
            self.len = function () {
                return realRequest.responseText.length;
            };

        };

        /**** createNativeRequest
            returns an XMLHttpRequest in most browsers.
        */
        /* Based on dojo/src/hostenv_browser.js */

        this.createNativeRequest = function () {
            /*

                subscribes to the lazy function definition pattern, since it
                redefines itself as the first method that works on the first
                call.

                Some other AJAX implementations check
                 - Msxml2.XMLHTTP.6.0
                 - Msxml2.XMLHTTP.5.0
                 - Msxml2.XMLHTTP.4.0
                 - Msxml2.XMLHTTP.3.0
                 - Microsoft.XMLHTTP

                Microsoft.XMLHTTP is an older name-space, but is equivalent to
                the more lucid Msxml2.XMLHTTP.3.0 and only available when the
                latter is available too.

                Msxml2.XMLHTTP.4.0 has been superseded and is currently only
                intended to support legacy applications.

                Msxml2.XMLHTTP.5.0 was shipped with MS Office 2003 and was
                intended for Office applications. IE7 has this component off
                by default in the Internet zone, leading to canary-yellow
                verification dialogs.

                Msxml2.XMLHTTP.6.0 is currently the standard MS is pushing.
                I originally left out 6.0 since it would increase the burden
                of testing for functionality that cannot be trusted to work
                in all browsers.
                However, I've taken Jonathan Snook's advice to check for
                Microsoft's latest and greatest.

                see: http://snook.ca/archives/javascript/xmlhttprequest_activex_ie/

                Msxml2.XMLHTTP.3.0 is the most widely deployed version and is
                serviced regularly with the OS for security and other reasons.
                It is MS's preferred alternative to MSXML6.

                see: http://blogs.msdn.com/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx

                see: http://www.telerik.com/documents/AJAX%20Page/Ajax-Part1.pdf page 3

            */

            var trials = [
                function () {return new XMLHttpRequest()},
                function () {return new ActiveXObject("Msxml2.XMLHTTP.6.0")},
                function () {return new ActiveXObject("Msxml2.XMLHTTP.3.0")},
                function () {throw new Error("No HTTP Request object available for your environment.")}
            ];

            var trial, result, exception;
            for (var i = 0; i < trials.length; i++) {
                exception = undefined;
                /* redeclare for posterity */
                httplib.createNativeRequest = trial = trials[i];
                try {
                    result = trial();
                } catch (trialException) {
                    exception = trialException;
                    continue;
                }
                break;
            }

            if (exception) throw exception;
            else return result;
        };

    };

    /*** readylib
    */
    var readylib = new function () {

        /**** beginTime
        */

        /**** readyTime
        */

        /**** endTime
        */

        /**** observe
        */
        this.observe = function (callback) {
            if (isReady) {
                callback();
            } else {
                callbacks.push(callback);
            }
        };

        var isReady;

        var callbacks = [];

        var ready = function () {
            isReady = true;
            for (var i = 0; i < callbacks.length; i++) {
                var callback = callbacks[i];
                callback();
            }
            callbacks = [];
        };

        /*
            Opera uses DOMContentLoaded but has special code for
            pending style sheets.
        */
        if (browserlib.isOpera)
            document.addEventListener("DOMContentLoaded", function () {
                if (isReady) {
                    return;
                }
                for (var i = 0; i < document.styleSheets.length; i++)
                    if (document.styleSheets[i].disabled) {
                        setTimeout(arguments.callee, 0);
                        return;
                    }
                // and execute any waiting functions
                ready();
            }, false);

        /* Mozilla and WebKit nightlies currently support this event */
        if (document.addEventListener) {
            /* Use the handy event callback */
            document.addEventListener("DOMContentLoaded", ready, false);
        }

        /*
            If IE is used and is not in a frame,
            continually check to see whether the document is ready.
        */
        if (browserlib.isIE && window == top) (function () {
            if (isReady) {
                return;
            }
            try {
                /*
                    If IE is used, use the trick by Diego Perini
                    http://javascript.nwbox.com/IEContentLoaded/
                */
                document.documentElement.doScroll("left");
            } catch (error) {
                /*
                    using setTimeout with a 0 milisecond dellay
                    is effectively the equivalent of a "yield"
                    in a cooperative multi-task language.
                    This permits the browser to breathe before
                    we check whether we're ready again.
                */
                setTimeout(arguments.callee, 0);
                return;
            }
            ready();
        })();

        if (browserlib.isWebkit) {
            (function () {
                if (isReady) {
                    return;
                }
                if (
                    document.readyState != "loaded" &&
                    document.readyState != "complete"
                ) {
                    setTimeout(arguments.callee, 0);
                    return;
                }
                var numStyles = document.getElementsByTagName('style').length;
                var links = document.getElementsByTagName('link');
                for (var i = 0; i < links.length; i++) {
                    var link = links[i];
                    numStyles += (
                        link.hasAttribute('rel') &&
                        link.getAttribute('rel').toLowerCase() ==
                        'stylesheet'
                    );
                }
                if (document.styleSheets.length != numStyles) {
                    setTimeout(arguments.callee, 0);
                    return;
                }
                ready();
            })();
        }

        /*
            for other browsers, give up on the time saving
            techniques and wait for all the images to load.
            also, do this in other browsers just in case they missed
            the boat.
        */
        if (window.onload) {
            /* if there's already an onload listener, call ready before it */
            window.onload = (function (onload) {
                return function () {
                    ready();
                    onload();
                };
            })(window.onload);
        } else {
            window.onload = ready;
        }

    };

    main();

})(function () {
    return eval.call(window, arguments[0]);
});

/*license

    Legal
    =======
    
    Chiron is a component of the Tale web-game project.
    
    See <credit.txt> for a complete list of
    contributions and their licenses.  All contributions are provided
    under permissive, non-viral licenses including MIT, BSD, Creative Commons
    Attribution 2.5, Public Domain, or Unrestricted.
    
    
    License
    =======
    
    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    
    MIT License
    -----------
    
    Permission is hereby granted, free of charge, to any person
    obtaining a copy of this software and associated documentation
    files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use,
    copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following
    conditions:
    
    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    OTHER DEALINGS IN THE SOFTWARE.

*/

